#include "t_my_httpbackend.h"

static QNetworkReply::NetworkError s_networkErrorCodeFromHttpStatus(int httpStatusCode, const QUrl &url);

T_MyHttpBackend::T_MyHttpBackend()
	: QNetworkAccessBackend()
	, m_networkReply(0)
	, m_readCounter(0)
{
}

T_MyHttpBackend::~T_MyHttpBackend()
{
	p_disconnectFromHttp();
}

/* virtual */ void T_MyHttpBackend::open()
{
	m_networkReply = m_nam.createRequest(
			QNetworkAccessBackend::operation(),
			QNetworkAccessBackend::request(),
			reply->outgoingData
			);
	m_networkReply->setParent(this);
	connect(&m_nam, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
			SLOT(authenticationRequired(QNetworkReply*,QAuthenticator*)));
	connect(m_networkReply, SIGNAL(metaDataChanged()), SLOT(replyHeaderChanged()));
	connect(m_networkReply, SIGNAL(readyRead()), SLOT(replyReadyRead()));
	connect(m_networkReply, SIGNAL(finished()), SLOT(replyFinished()));
	connect(m_networkReply, SIGNAL(error(QNetworkReply::NetworkError)),
			SLOT(replyError(QNetworkReply::NetworkError)));
}

/* virtual */ void T_MyHttpBackend::closeDownstreamChannel()
{
	// this indicates that the user closed the stream while the reply isn't finished yet
}

/* virtual */ bool T_MyHttpBackend::waitForDownstreamReadyRead(int msecs)
{
	Q_ASSERT(m_networkReply);

	if (m_networkReply->bytesAvailable()) {
		p_readFromHttp();
		return true;
	}
	if (msecs == 0) {
		// no bytes available in the socket and no waiting
		return false;
	}
	// ### FIXME
	qCritical("QNetworkAccess: HTTP backend does not support waitForReadyRead()");
	return false;
}

/* virtual */ void T_MyHttpBackend::downstreamReadyWrite()
{
	p_readFromHttp();
	if (m_networkReply && m_networkReply->bytesAvailable() == 0 && m_networkReply->isFinished())
		T_MyHttpBackend::replyFinished();
}

/* virtual */ void T_MyHttpBackend::setDownstreamLimited(bool b)
{
	Q_UNUSED(b);
}

/* virtual */ void T_MyHttpBackend::copyFinished(QIODevice *dev)
{
	delete dev;
	T_MyHttpBackend::finished();
}

/* slot */ void T_MyHttpBackend::finished()
{
	p_disconnectFromHttp();
	QNetworkAccessBackend::finished();
}

/* slot */ void T_MyHttpBackend::replyReadyRead()
{
	p_readFromHttp();
}

/* slot */ void T_MyHttpBackend::replyFinished()
{
	if (m_networkReply->bytesAvailable())
		// we haven't read everything yet. Wait some more.
		return;

	int statusCode = m_networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
	if (statusCode >= 400) {
		// it's an error reply
		QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
													  "Error downloading %1 - server replied: %2"));
		msg = msg.arg(url().toString(), m_networkReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString());
		QNetworkAccessBackend::error(
				s_networkErrorCodeFromHttpStatus(statusCode, url()),
				msg);
	}

	T_MyHttpBackend::finished();
}

/* slot */ void T_MyHttpBackend::replyHeaderChanged()
{
	QList<QByteArray> v_headerList = m_networkReply->rawHeaderList();
	foreach(QByteArray v_first, v_headerList)
	{
		QByteArray v_second = m_networkReply->rawHeader(v_first);
		qDebug() << "T_MyHttpBackend::replyHeaderChanged():" << v_first << v_second;
		QNetworkAccessBackend::setRawHeader(v_first, v_second);
	}

	QNetworkAccessBackend::setAttribute(
			QNetworkRequest::HttpStatusCodeAttribute,
			m_networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute));
	QNetworkAccessBackend::setAttribute(
			QNetworkRequest::HttpReasonPhraseAttribute,
			m_networkReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute));

	// is it a redirection?
	const int statusCode = m_networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
	p_checkForRedirect(statusCode);

	if (statusCode != 304 && statusCode != 303) {
		if (!isCachingEnabled())
			setCachingEnabled(true);
	}
	QNetworkAccessBackend::metaDataChanged();
}

/* slot */ void T_MyHttpBackend::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
{
	Q_UNUSED(reply);
	QNetworkAccessBackend::authenticationRequired(authenticator);
}

/* slot */ void T_MyHttpBackend::replyError(QNetworkReply::NetworkError errorCode)
{
	QNetworkReply *v_reply = dynamic_cast<QNetworkReply *>(QObject::sender());
	QString errorString = v_reply ? v_reply->errorString() : QString::number(errorCode);
	qDebug() << errorCode << errorString;
	QNetworkAccessBackend::error(errorCode, errorString);
	finished();
}

void T_MyHttpBackend::p_disconnectFromHttp()
{
#if 0x0
	if (m_networkReply)
	{
		m_networkReply->abort();
	}
#endif
}

void T_MyHttpBackend::p_readFromHttp()
{
	if (!m_networkReply)
		return;
	// We read possibly more than nextDownstreamBlockSize(), but
	// this is not a critical thing since it is already in the
	// memory anyway
	QByteDataBuffer list;
	while (m_networkReply->bytesAvailable() != 0
		   && QNetworkAccessBackend::nextDownstreamBlockSize() != 0
		   && QNetworkAccessBackend::nextDownstreamBlockSize() > list.byteAmount()) {
		QByteArray v_bytes =
				m_networkReply->read(
						QNetworkAccessBackend::nextDownstreamBlockSize() - list.byteAmount());
		m_readCounter += v_bytes.size();
		qDebug() << "[m_readCounter]" << m_readCounter;
		list.append(v_bytes);
	}
	if (!list.isEmpty())
		QNetworkAccessBackend::writeDownstreamData(list);
}

void T_MyHttpBackend::p_checkForRedirect(const int statusCode)
{
	switch (statusCode) {
	case 301:				   // Moved Permanently
	case 302:				   // Found
	case 303:				   // See Other
	case 307:				   // Temporary Redirect
		// What do we do about the caching of the HTML note?
		// The response to a 303 MUST NOT be cached, while the response to
		// all of the others is cacheable if the headers indicate it to be
		QByteArray header = QNetworkAccessBackend::rawHeader("location");
		QUrl url = QUrl::fromEncoded(header);
		if (!url.isValid())
			url = QUrl(QLatin1String(header));
		QNetworkAccessBackend::redirectionRequested(url);
	}
}

static QNetworkReply::NetworkError s_networkErrorCodeFromHttpStatus(int httpStatusCode, const QUrl &url)
{
	QNetworkReply::NetworkError code;
	// we've got an error
	switch (httpStatusCode) {
	case 401:			   // Authorization required
		code = QNetworkReply::AuthenticationRequiredError;
		break;

	case 403:			   // Access denied
		code = QNetworkReply::ContentOperationNotPermittedError;
		break;

	case 404:			   // Not Found
		code = QNetworkReply::ContentNotFoundError;
		break;

	case 405:			   // Method Not Allowed
		code = QNetworkReply::ContentOperationNotPermittedError;
		break;

	case 407:
		code = QNetworkReply::ProxyAuthenticationRequiredError;
		break;

	default:
		if (httpStatusCode > 500) {
			// some kind of server error
			code = QNetworkReply::ProtocolUnknownError;
		} else if (httpStatusCode >= 400) {
			// content error we did not handle above
			code = QNetworkReply::UnknownContentError;
		} else {
			qWarning("QNetworkAccess: got HTTP status code %d which is not expected from url: \"%s\"",
					 httpStatusCode, qPrintable(url.toString()));
			code = QNetworkReply::ProtocolFailure;
		}
	}

	return code;
}
